import { watch } from 'vue'
import { PiniaPluginContext } from 'pinia'

export interface StorageInterface {
    getItem(key: string): string | null
    setItem(key: string, value: string): void
    [name: string]: unknown
}
export interface PersistStrategy {
    key?: string
    storage?: StorageInterface
    paths?: string[]
}

export interface PersistOptions {
    enabled: true
    strategies?: PersistStrategy[]
}

type Store = PiniaPluginContext['store']
type PartialState = Partial<Store['$state']>

declare module 'pinia' {
    export interface DefineStoreOptionsBase<S, Store> {
        persist?: PersistOptions
    }
}

const updateStorage = (strategy: PersistStrategy, store: Store) => {
    const storage = strategy.storage
    const storeKey = strategy.key || store.$id

    if (!storage) throw new Error('请填写storage')

    if (strategy.paths) {
        const partialState = strategy.paths.reduce((finalObj, key) => {
            finalObj[key] = store.$state[key]
            return finalObj
        }, {} as PartialState)

        storage.setItem(storeKey, JSON.stringify(partialState))
    } else {
        storage.setItem(storeKey, JSON.stringify(store.$state))
    }
}

export default ({ options, store }: PiniaPluginContext): void => {
    if (options.persist?.enabled) {
        const defaultStrat: PersistStrategy[] = [
            {
                key: store.$id,
            },
        ]
        const strategies = options.persist?.strategies?.length
            ? options.persist?.strategies
            : defaultStrat
        strategies.forEach((strategy: PersistStrategy) => {
            const storage = strategy.storage || sessionStorage
            const storeKey = strategy.key || store.$id
            const storageResult = storage.getItem(storeKey)

            if (storageResult) {
                store.$patch(JSON.parse(storageResult))
                updateStorage(strategy, store)
            }
        })

        watch(
            () => store.$state,
            () => {
                strategies.forEach((strategy: PersistStrategy) => {
                    updateStorage(strategy, store)
                })
            },
            { deep: true }
        )
    }
}
